package graph;

import edu.uci.ics.jung.graph.DirectedSparseGraph;
import edu.uci.ics.jung.graph.Graph;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import constraint.Constraint;
import constraint.ConstraintSimple;

/**
 * A subgraph is the graph (edges and nodes) that are children to the same parent
 * in the resolution (Product, Assembly, Part, Detail)  hierarchy.
 * 
 * @author jay
 *
 */
public class SubGGraph {
    GNode parent;
    private DirectedSparseGraph<GNode, Constraint> productGraph;
    private DirectedSparseGraph<GNode, Constraint> assemblyGraph;
    private DirectedSparseGraph<GNode, Constraint> partGraph;
    private DirectedSparseGraph<GNode, Constraint> detailGraph;
    
    private DirectedSparseGraph<GNode, Constraint> connectProductToAssembly;
    private DirectedSparseGraph<GNode, Constraint> connectAssemblyToPart;
    private DirectedSparseGraph<GNode, Constraint> connectPartToDetail;

    public SubGGraph(GNode parent) {
        productGraph = new DirectedSparseGraph<GNode, Constraint>();
        assemblyGraph = new DirectedSparseGraph<GNode, Constraint>();
        partGraph = new DirectedSparseGraph<GNode, Constraint>();
        detailGraph = new DirectedSparseGraph<GNode, Constraint>();

        connectProductToAssembly = new DirectedSparseGraph<GNode, Constraint>();
        connectAssemblyToPart = new DirectedSparseGraph<GNode, Constraint>();
        connectPartToDetail = new DirectedSparseGraph<GNode, Constraint>();
        this.parent = parent;
    }
    
    public String toString() {
        String s = "";
        s = s.concat("\nParent ");
        s = s.concat(parent.toString());
        s = s.concat("\nProduct Graph ");
        s = s.concat(productGraph.toString());
        s = s.concat("\nAssembly Graph" );
        s = s.concat(this.assemblyGraph.toString());
        s = s.concat("\nPart Graph ");
        s = s.concat(this.partGraph.toString());
        s = s.concat("\nConect Graph Product to Assembly ");
        s = s.concat(this.connectProductToAssembly.toString());
        s = s.concat("\nConect Graph Assembly to Part");
        s = s.concat(this.connectAssemblyToPart.toString());
        return s;
    }
    
    public Constraint addParent(GNode child, GNode parent) {
        Constraint edge = null;
        if (parent.getType() == NodeType.PRODUCT) {
            edge = new ConstraintSimple(EdgeType.PRODUCT_TO_ASSEMBLY);
            this.connectProductToAssembly.addEdge(edge, parent, child);
        }
        if (parent.getType() == NodeType.ASSEMBLY) {
            edge = new ConstraintSimple(EdgeType.ASSEMBLY_TO_PART);
            this.connectAssemblyToPart.addEdge(edge, parent, child);
        }
        if (parent.getType() == NodeType.PART) {
            edge = new ConstraintSimple(EdgeType.PART_TO_DETAIL);
            this.connectPartToDetail.addEdge(edge, parent, child);
        }
        return edge;
    }

    
    public Constraint addEdge(GNode fromNode, GNode toNode) {
        if(fromNode.getType() != toNode.getType()) {
            System.out.println("Trying to add an edge between nodes that aren't compatible types.");
            return null;
        }

        Constraint edge = null;
        if (fromNode.getType() == NodeType.PRODUCT) {
            edge = new ConstraintSimple(EdgeType.PRODUCT);
            productGraph.addEdge(edge, fromNode, toNode);
        } else if (fromNode.getType() == NodeType.ASSEMBLY) {
            edge = new ConstraintSimple(EdgeType.ASSEMBLY);
            assemblyGraph.addEdge(edge, fromNode, toNode);
        } else if (fromNode.getType() == NodeType.PART) {
            edge = new ConstraintSimple(EdgeType.PART);
            partGraph.addEdge(edge, fromNode, toNode);
        } else if (fromNode.getType() == NodeType.DETAIL) {
            edge = new ConstraintSimple(EdgeType.DETAIL);
            detailGraph.addEdge(edge, fromNode, toNode);
        } 
        return edge;
    }

    public int getNodeCount(NodeType type) {
        return getGraphOfType(type).getVertexCount();
    }
    
    public int getNodeCount() {
        return this.productGraph.getVertexCount() + this.assemblyGraph.getVertexCount() 
        + this.partGraph.getVertexCount() + this.detailGraph.getVertexCount();
    }
    
    public Collection<GNode>getNodes(NodeType type) {
        return getGraphOfType(type).getVertices();
    }
    
    public Collection<Constraint>getEdges(EdgeType type) {
        return getGraphOfType(type).getEdges();
    }
    
    public Graph<GNode, Constraint>getGraphOfType(EdgeType type) {
        Graph<GNode, Constraint> graph = null;
        
        if (type == EdgeType.PRODUCT) {
            graph = productGraph;
        } else if (type == EdgeType.ASSEMBLY) {
            graph = assemblyGraph;
        } else if (type == EdgeType.PART) {
            graph = partGraph;
        } 
//        else if (type == EdgeType.DETAIL) {
//            graph=detailGraph;
//        }
        return graph;
    }
    
    public Graph<GNode, Constraint>getGraphOfType(NodeType type) {
        Graph<GNode, Constraint> graph = null;
        
        if (type == NodeType.PRODUCT) {
            graph = productGraph;
        } else if (type == NodeType.ASSEMBLY) {
            graph = assemblyGraph;
        } else if (type == NodeType.PART) {
            graph = partGraph;
        } 
//        else if (type == NodeType.DETAIL) {
//            graph=detailGraph;
//        }
        return graph;
    }
    
    public void print() {
        System.out.println(toString());
    }

    public DirectedSparseGraph<GNode, Constraint> getConnectGraphOfType(NodeType type) {
        DirectedSparseGraph<GNode, Constraint> graph = null;
        if (type == NodeType.PRODUCT) {
            graph = this.connectProductToAssembly;
        } else if (type == NodeType.ASSEMBLY) {
            graph = this.connectAssemblyToPart;
        } else if (type == NodeType.PART) {
            graph = this.connectPartToDetail;
        } 
        return graph;
    }

    public void addNode(GNode node) {
        getGraphOfType(node.getType()).addVertex(node);
    }

    public void removeNode(GNode node) {
        productGraph.removeVertex(node);
        assemblyGraph.removeVertex(node);
        partGraph.removeVertex(node);
//        detailGraph.removeVertex(node);

        connectProductToAssembly.removeVertex(node);
        connectAssemblyToPart.removeVertex(node);
        connectPartToDetail.removeVertex(node);
    }

    public boolean hasNode(GNode node) {
        if (node.getType() == NodeType.PRODUCT) {
            return this.productGraph.containsVertex(node);
        } else if (node.getType() == NodeType.ASSEMBLY) {
            return this.assemblyGraph.containsVertex(node);
        } else if (node.getType() == NodeType.PART) {
            return this.partGraph.containsVertex(node);
        } 
//        else if (node.type == NodeType.DETAIL) {
//                return this.detailGraph.containsVertex(node);
//        }
        return false;
    }

    public Collection<GNode> getSameLevelNeighbors(GNode node) {
        DirectedSparseGraph<GNode, Constraint> g=null;
        if (node.getType() == NodeType.PRODUCT) {
            g = this.productGraph;
        } else if (node.getType() == NodeType.ASSEMBLY) {
            g = this.assemblyGraph;
        } else if (node.getType() == NodeType.PART) {
            g = this.partGraph;
        } 
//        else if (node.type == NodeType.DETAIL) {
//            g = this.detailGraph;
//        }
        return g.getNeighbors(node);
    }

    public List<GNode> getNodes() {
        List<GNode> nodes = new ArrayList<GNode>();
        nodes.addAll(this.partGraph.getVertices());
        nodes.addAll(this.assemblyGraph.getVertices());
        nodes.addAll(this.productGraph.getVertices());
//        nodes.addAll(this.detailGraph.getVertices());
        return nodes;
    }

}
